code: package org.gridkit.jvmtool.bstub;
  
  import java.io.File;
  import java.io.FileOutputStream;
  import java.io.IOException;
  import java.security.MessageDigest;
  import java.security.NoSuchAlgorithmException;
  import java.util.jar.Attributes;
  import java.util.jar.Manifest;
  
  public class AgentHelper {
  
      private static File agentJar; 
      
      public static synchronized String initAgentJar() throws IOException {
          if (agentJar != null) {
              return agentJar.getCanonicalPath();
          }
          
          File t = File.createTempFile("___", "_");
          File tdir = t.getParentFile();
          File tt = new File(tdir, "btpack");
          tt.mkdir();
          
          byte[] agent = createAgentJar();
          
          String hash = digest(agent, "SHA-256");
          
          File name = new File(tt, "agent-" + hash + ".jar");
  
          if (name.isFile() && name.length() == agent.length) {
              agentJar = name;
              return name.getCanonicalPath();
          }
          
          name.delete();
          
          FileOutputStream fos = new FileOutputStream(name);
          fos.write(agent);
          fos.close();
          
          agentJar = name;
          
          return name.getCanonicalPath();
      }
  
      static byte[] createAgentJar() throws IOException {
          
          JarBuilderTool builder = new JarBuilderTool();
          Manifest mf = builder.getManifest();
          
          mf.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0");
          mf.getMainAttributes().put(new Attributes.Name("Agent-Class"), "net.java.btrace.agent.Main");
          mf.getMainAttributes().put(new Attributes.Name("Can-Redefine-Classes"), "true");
          mf.getMainAttributes().put(new Attributes.Name("Can-Retransform-Classes"), "true");
          
          builder.addService("net.java.btrace.spi.wireio.CommandImpl");
          builder.addService("net.java.btrace.spi.server.ServerImpl");
          
          builder.addPackage("net.java.btrace.agent");   
          builder.addPackage("net.java.btrace.agent.wireio");   
          builder.addPackage("net.java.btrace.annotations");   
          builder.addPackage("net.java.btrace.api.core");   
          builder.addPackage("net.java.btrace.api.extensions");   
          builder.addPackage("net.java.btrace.api.extensions.runtime");   
          builder.addPackage("net.java.btrace.api.extensions.util");   
          builder.addPackage("net.java.btrace.api.server");
          builder.addPackage("net.java.btrace.api.types");
          builder.addPackage("net.java.btrace.api.wireio");
          builder.addPackage("net.java.btrace.commands");   
          builder.addPackage("net.java.btrace.ext");   
          builder.addPackage("net.java.btrace.instr");   
          builder.addPackage("net.java.btrace.org.objectweb.asm");   
          builder.addPackage("net.java.btrace.org.objectweb.asm.commons");   
          builder.addPackage("net.java.btrace.org.objectweb.asm.signature");   
          builder.addPackage("net.java.btrace.resources");   
          builder.addPackage("net.java.btrace.runtime");   
          builder.addPackage("net.java.btrace.server");   
          builder.addPackage("net.java.btrace.spi");   
          builder.addPackage("net.java.btrace.spi.server");   
          builder.addPackage("net.java.btrace.spi.wireio");   
          builder.addPackage("net.java.btrace.util");
          builder.addPackage("net.java.btrace.server.wireio");   
          builder.addPackage("net.java.btrace.wireio.commands");   
  
          builder.addPackage("org.gridkit.nimble.btrace.ext");   
          
          byte[] data = builder.collectJar();
          
          return data;
      }
  
      private static String digest(byte[] data, String algorithm) {
          try {
              MessageDigest md = MessageDigest.getInstance(algorithm);
              byte[] digest = md.digest(data);
              StringBuilder buf = new StringBuilder();
              for(byte b: digest) {
                  buf.append(Integer.toHexString(0xF & (b >> 4)));
                  buf.append(Integer.toHexString(0xF & (b)));
              }
              return buf.toString();
          } catch (NoSuchAlgorithmException e) {
              throw new IllegalArgumentException(e);
          }
      }
  }
  