/*
* ApplicationInsights-Java
* Copyright (c) Microsoft Corporation
* All rights reserved.
*
* MIT License
* Permission is hereby granted, free of charge, to any person obtaining a copy of this
* software and associated documentation files (the ""Software""), to deal in the Software
* without restriction, including without limitation the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software, and to permit
* persons to whom the Software is furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all copies or
* substantial portions of the Software.
* THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
* PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
* FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
package com.microsoft.applicationinsights.agent.internal.agent;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.lang.instrument.Instrumentation;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.net.URLDecoder;
import java.util.Enumeration;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import com.microsoft.applicationinsights.agent.internal.agent.jmx.JmxConnectorLoader;
import com.microsoft.applicationinsights.agent.internal.common.StringUtils;
import com.microsoft.applicationinsights.agent.internal.config.AgentConfiguration;
import com.microsoft.applicationinsights.agent.internal.config.AgentConfigurationBuilderFactory;
import com.microsoft.applicationinsights.agent.internal.config.DataOfConfigurationForException;
import com.microsoft.applicationinsights.agent.internal.coresync.impl.ImplementationsCoordinator;
import com.microsoft.applicationinsights.agent.internal.logger.InternalAgentLogger;
/**
* Created by gupele on 5/6/2015.
*/
public final class AgentImplementation {
private final static String AGENT_JAR_PREFIX = "applicationinsights-agent";
private final static String CORE_JAR_PREFIX = "applicationinsights-core";
private final static String DISTRIBUTION_JAR_PREFIX = "applicationinsights-all";
private final static String CORE_SELF_REGISTRATOR_CLASS_NAME = "com.microsoft.applicationinsights.internal.agent.AgentSelfConnector";
private final static String CORE_SELF_SHORT_REGISTRATOR_CLASS_NAME = "AgentSelfConnector";
private static String agentJarLocation;
public static void premain(String args, Instrumentation inst) {
try {
agentJarLocation = getAgentJarLocation();
appendJarsToBootstrapClassLoader(inst);
initializeCodeInjector(inst);
} catch (Throwable throwable) {
InternalAgentLogger.INSTANCE.logAlways(InternalAgentLogger.LoggingLevel.ERROR, "Agent is NOT activated: failed to load to bootstrap class loader: " + throwable.getMessage());
throwable.printStackTrace();
System.exit(-1);
}
}
@SuppressWarnings("unchecked")
private static void initializeCodeInjector(Instrumentation inst) throws Throwable {
ClassLoader bcl = AgentImplementation.class.getClassLoader().getParent();
Class<CodeInjector> cic = (Class<CodeInjector>) bcl.loadClass("com.microsoft.applicationinsights.agent.internal.agent.CodeInjector");
if (cic == null) {
throw new IllegalStateException("Failed to load CodeInjector");
}
AgentConfiguration agentConfiguration = new AgentConfigurationBuilderFactory().createDefaultBuilder().parseConfigurationFile(agentJarLocation);
if (agentConfiguration.isSelfRegistrationMode()) {
SetNonWebAppModeIfAskedByConf(agentConfiguration.getSdkPath());
}
try {
CodeInjector codeInjector = cic.getDeclaredConstructor(AgentConfiguration.class).newInstance(agentConfiguration);
DataOfConfigurationForException exceptionData = agentConfiguration.getBuiltInConfiguration().getDataOfConfigurationForException();
if (inst.isRetransformClassesSupported()) {
if (exceptionData.isEnabled()) {
InternalAgentLogger.INSTANCE.logAlways(InternalAgentLogger.LoggingLevel.TRACE, "Instrumenting runtime exceptions.");
inst.addTransformer(codeInjector, true);
ImplementationsCoordinator.INSTANCE.setExceptionData(exceptionData);
inst.retransformClasses(RuntimeException.class);
inst.removeTransformer(codeInjector);
}
} else {
if (exceptionData.isEnabled()) {
InternalAgentLogger.INSTANCE.logAlways(InternalAgentLogger.LoggingLevel.TRACE, "The JVM does not support re-transformation of classes.");
}
}
inst.addTransformer(codeInjector);
} catch (Exception e) {
InternalAgentLogger.INSTANCE.logAlways(InternalAgentLogger.LoggingLevel.ERROR, "Failed to load the code injector, exception: %s", e.getMessage());
throw e;
}
}
private static void appendJarsToBootstrapClassLoader(Instrumentation inst) throws Throwable {
String agentJarPath = agentJarLocation.startsWith("file:/") ? agentJarLocation : new File(agentJarLocation).toURI().toString();
String agentJarName = null;
File agentFolder = new File(agentJarLocation);
for (File file : agentFolder.listFiles()) {
if (file.getName().indexOf(AGENT_JAR_PREFIX) != -1) {
agentJarName = file.getName();
break;
}
}
if (agentJarName == null) {
throw new RuntimeException("Could not find agent jar");
}
InternalAgentLogger.INSTANCE.info("Found jar: " + agentJarPath + " " + agentJarName);
URL configurationURL = new URL(agentJarPath + agentJarName);
JarFile agentJar = new JarFile(URLDecoder.decode(configurationURL.getFile(), "UTF-8"));
inst.appendToBootstrapClassLoaderSearch(agentJar);
InternalAgentLogger.INSTANCE.logAlways(InternalAgentLogger.LoggingLevel.TRACE, "Successfully loaded Agent jar");
}
public static String getAgentJarLocation() throws UnsupportedEncodingException {
try {
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
if ((systemClassLoader instanceof URLClassLoader)) {
for (URL url : ((URLClassLoader)systemClassLoader).getURLs()) {
String urlPath = url.getPath();
if (urlPath.indexOf(AGENT_JAR_PREFIX) != -1) {
int index = urlPath.lastIndexOf('/');
urlPath = urlPath.substring(0, index + 1);
return urlPath;
}
}
}
} catch (Throwable throwable) {
InternalAgentLogger.INSTANCE.logAlways(InternalAgentLogger.LoggingLevel.ERROR, "Error while trying to fetch Jar Location, Exception: " + throwable.getMessage());
}
String path = AgentImplementation.class.getProtectionDomain().getCodeSource().getLocation().getPath();
return URLDecoder.decode(path, "UTF-8");
}
private static void SetNonWebAppModeIfAskedByConf(String sdkPath) throws Throwable {
String path = sdkPath;
if (StringUtils.isNullOrEmpty(path)) {
path = agentJarLocation;
}
File sdkFolder = new File(path);
if (!sdkFolder.exists()) {
String errorMessage = String.format("Path %s for core jar does not exist", path);
InternalAgentLogger.INSTANCE.logAlways(InternalAgentLogger.LoggingLevel.ERROR, errorMessage);
throw new Exception(errorMessage);
}
if (!sdkFolder.isDirectory()) {
String errorMessage = String.format("Path %s for core jar must be a folder", path);
InternalAgentLogger.INSTANCE.logAlways(InternalAgentLogger.LoggingLevel.ERROR, errorMessage);
throw new Exception(errorMessage);
}
if (!sdkFolder.canRead()) {
String errorMessage = String.format("Path %s for core jar must be a folder that can be read", path);
InternalAgentLogger.INSTANCE.logAlways(InternalAgentLogger.LoggingLevel.ERROR, errorMessage);
throw new Exception(errorMessage);
}
InternalAgentLogger.INSTANCE.logAlways(InternalAgentLogger.LoggingLevel.TRACE, "Found %s", path);
String coreJarName = null;
for (File file : sdkFolder.listFiles()) {
if (file.getName().indexOf(CORE_JAR_PREFIX) != -1 || file.getName().indexOf(DISTRIBUTION_JAR_PREFIX) != -1) {
coreJarName = file.getAbsolutePath();
InternalAgentLogger.INSTANCE.logAlways(InternalAgentLogger.LoggingLevel.TRACE, "Found core jar: %s", coreJarName);
break;
}
}
if (coreJarName == null) {
String errorMessage = String.format("Did not find core jar in path %s", path);
InternalAgentLogger.INSTANCE.logAlways(InternalAgentLogger.LoggingLevel.ERROR, errorMessage);
throw new Exception(errorMessage);
}
InternalAgentLogger.INSTANCE.logAlways(InternalAgentLogger.LoggingLevel.TRACE, "Found jar: " + coreJarName);
JarFile jarFile = null;
try {
jarFile = new JarFile(coreJarName);
} catch (IOException e) {
InternalAgentLogger.INSTANCE.logAlways(InternalAgentLogger.LoggingLevel.ERROR, "Could not load jar: " + coreJarName);
throw e;
}
Enumeration<JarEntry> e = jarFile.entries();
URL[] urls = { new URL("jar:file:" + coreJarName+"!/") };
URLClassLoader cl = URLClassLoader.newInstance(urls);
while (e.hasMoreElements()) {
JarEntry je = e.nextElement();
if(je.isDirectory() || !je.getName().endsWith(".class")){
continue;
}
try {
Class clazz = cl.loadClass(CORE_SELF_REGISTRATOR_CLASS_NAME);
clazz.getDeclaredConstructor().newInstance();
InternalAgentLogger.INSTANCE.logAlways(InternalAgentLogger.LoggingLevel.TRACE, "Loaded core jar");
break;
} catch (ClassNotFoundException e1) {
InternalAgentLogger.INSTANCE.logAlways(InternalAgentLogger.LoggingLevel.ERROR, "Could not load class: %s, ClassNotFoundException", CORE_SELF_SHORT_REGISTRATOR_CLASS_NAME);
throw e1;
} catch (InvocationTargetException e1) {
InternalAgentLogger.INSTANCE.logAlways(InternalAgentLogger.LoggingLevel.ERROR, "Could not load class: %s, InvocationTargetException", CORE_SELF_SHORT_REGISTRATOR_CLASS_NAME);
throw e1;
} catch (NoSuchMethodException e1) {
InternalAgentLogger.INSTANCE.logAlways(InternalAgentLogger.LoggingLevel.ERROR, "Could not load class: %s, NoSuchMethodException", CORE_SELF_SHORT_REGISTRATOR_CLASS_NAME);
throw e1;
} catch (InstantiationException e1) {
InternalAgentLogger.INSTANCE.logAlways(InternalAgentLogger.LoggingLevel.ERROR, "Could not load class: %s, InstantiationException", CORE_SELF_SHORT_REGISTRATOR_CLASS_NAME);
throw e1;
} catch (IllegalAccessException e1) {
InternalAgentLogger.INSTANCE.logAlways(InternalAgentLogger.LoggingLevel.ERROR, "Could not load class: %s, IllegalAccessException", CORE_SELF_SHORT_REGISTRATOR_CLASS_NAME);
throw e1;
}
}
}
}