content: package com.taobao.arthas.agent334;
  
  import java.arthas.SpyAPI;
  import java.io.File;
  import java.io.FileOutputStream;
  import java.io.PrintStream;
  import java.io.UnsupportedEncodingException;
  import java.lang.instrument.Instrumentation;
  import java.net.URL;
  import java.net.URLDecoder;
  import java.security.CodeSource;
  
  import com.taobao.arthas.agent.ArthasClassloader;
  
  /**
   * 代理启动类
   *
   * @author vlinux on 15/5/19.
   */
  public class AgentBootstrap {
      private static final String ARTHAS_CORE_JAR = "arthas-core.jar";
      private static final String ARTHAS_BOOTSTRAP = "com.taobao.arthas.core.server.ArthasBootstrap";
      private static final String GET_INSTANCE = "getInstance";
      private static final String IS_BIND = "isBind";
  
      private static PrintStream ps = System.err;
      static {
          try {
              File arthasLogDir = new File(System.getProperty("user.home") + File.separator + "logs" + File.separator
                      + "arthas" + File.separator);
              if (!arthasLogDir.exists()) {
                  arthasLogDir.mkdirs();
              }
              if (!arthasLogDir.exists()) {
                  // #572
                  arthasLogDir = new File(System.getProperty("java.io.tmpdir") + File.separator + "logs" + File.separator
                          + "arthas" + File.separator);
                  if (!arthasLogDir.exists()) {
                      arthasLogDir.mkdirs();
                  }
              }
  
              File log = new File(arthasLogDir, "arthas.log");
  
              if (!log.exists()) {
                  log.createNewFile();
              }
              ps = new PrintStream(new FileOutputStream(log, true));
          } catch (Throwable t) {
              t.printStackTrace(ps);
          }
      }
  
      /**
       * <pre>
       * 1. 全局持有classloader用于隔离 Arthas 实现，防止多次attach重复初始化
       * 2. ClassLoader在arthas停止时会被reset
       * 3. 如果ClassLoader一直没变，则 com.taobao.arthas.core.server.ArthasBootstrap#getInstance 返回结果一直是一样的
       * </pre>
       */
      private static volatile ClassLoader arthasClassLoader;
  
      public static void premain(String args, Instrumentation inst) {
          main(args, inst);
      }
  
      public static void agentmain(String args, Instrumentation inst) {
          main(args, inst);
      }
  
      /**
       * 让下次再次启动时有机会重新加载
       */
      public static void resetArthasClassLoader() {
          arthasClassLoader = null;
      }
  
      private static ClassLoader getClassLoader(Instrumentation inst, File arthasCoreJarFile) throws Throwable {
          // 构造自定义的类加载器，尽量减少Arthas对现有工程的侵蚀
          return loadOrDefineClassLoader(arthasCoreJarFile);
      }
  
      private static ClassLoader loadOrDefineClassLoader(File arthasCoreJarFile) throws Throwable {
          if (arthasClassLoader == null) {
              arthasClassLoader = new ArthasClassloader(new URL[]{arthasCoreJarFile.toURI().toURL()});
          }
          return arthasClassLoader;
      }
  
      private static synchronized void main(String args, final Instrumentation inst) {
          // 尝试判断arthas是否已在运行，如果是的话，直接就退出
          try {
              Class.forName("java.arthas.SpyAPI"); // 加载不到会抛异常
              if (SpyAPI.isInited()) {
                  ps.println("Arthas server already stared, skip attach.");
                  ps.flush();
                  return;
              }
          } catch (Throwable e) {
              // ignore
          }
          try {
              ps.println("Arthas server agent start...");
              // 传递的args参数分两个部分:arthasCoreJar路径和agentArgs, 分别是Agent的JAR包路径和期望传递到服务端的参数
              if (args == null) {
                  args = "";
              }
              args = decodeArg(args);
  
              String arthasCoreJar;
              final String agentArgs;
              int index = args.indexOf(';');
              if (index != -1) {
                  arthasCoreJar = args.substring(0, index);
                  agentArgs = args.substring(index);
              } else {
                  arthasCoreJar = "";
                  agentArgs = args;
              }
  
              File arthasCoreJarFile = new File(arthasCoreJar);
              if (!arthasCoreJarFile.exists()) {
                  ps.println("Can not find arthas-core jar file from args: " + arthasCoreJarFile);
                  // try to find from arthas-agent.jar directory
                  CodeSource codeSource = AgentBootstrap.class.getProtectionDomain().getCodeSource();
                  if (codeSource != null) {
                      try {
                          File arthasAgentJarFile = new File(codeSource.getLocation().toURI().getSchemeSpecificPart());
                          arthasCoreJarFile = new File(arthasAgentJarFile.getParentFile(), ARTHAS_CORE_JAR);
                          if (!arthasCoreJarFile.exists()) {
                              ps.println("Can not find arthas-core jar file from agent jar directory: " + arthasAgentJarFile);
                          }
                      } catch (Throwable e) {
                          ps.println("Can not find arthas-core jar file from " + codeSource.getLocation());
                          e.printStackTrace(ps);
                      }
                  }
              }
              if (!arthasCoreJarFile.exists()) {
                  return;
              }
  
              /**
               * Use a dedicated thread to run the binding logic to prevent possible memory leak. #195
               */
              final ClassLoader agentLoader = getClassLoader(inst, arthasCoreJarFile);
  
              Thread bindingThread = new Thread() {
                  @Override
                  public void run() {
                      try {
                          bind(inst, agentLoader, agentArgs);
                      } catch (Throwable throwable) {
                          throwable.printStackTrace(ps);
                      }
                  }
              };
  
              bindingThread.setName("arthas-binding-thread");
              bindingThread.start();
              bindingThread.join();
          } catch (Throwable t) {
              t.printStackTrace(ps);
              try {
                  if (ps != System.err) {
                      ps.close();
                  }
              } catch (Throwable tt) {
                  // ignore
              }
              throw new RuntimeException(t);
          }
      }
  
      private static void bind(Instrumentation inst, ClassLoader agentLoader, String args) throws Throwable {
          /**
           * <pre>
           * ArthasBootstrap bootstrap = ArthasBootstrap.getInstance(inst);
           * </pre>
           */
          Class<?> bootstrapClass = agentLoader.loadClass(ARTHAS_BOOTSTRAP);
          Object bootstrap = bootstrapClass.getMethod(GET_INSTANCE, Instrumentation.class, String.class).invoke(null, inst, args);
          boolean isBind = (Boolean) bootstrapClass.getMethod(IS_BIND).invoke(bootstrap);
          if (!isBind) {
              String errorMsg = "Arthas server port binding failed! Please check $HOME/logs/arthas/arthas.log for more details.";
              ps.println(errorMsg);
              throw new RuntimeException(errorMsg);
          }
          ps.println("Arthas server already bind.");
      }
  
      private static String decodeArg(String arg) {
          try {
              return URLDecoder.decode(arg, "utf-8");
          } catch (UnsupportedEncodingException e) {
              return arg;
          }
      }
  }
  