@Override
 public void handleChanges(Collection<WatchServiceFileSystemWatcher.FileChangeEvent> changes) {
   try {
     List<AddedClass> addedClasses = new ArrayList<>();
     List<ClassDefinition> changedClasses = new ArrayList<>();
     for (WatchServiceFileSystemWatcher.FileChangeEvent change : changes) {
       if (change.getType() == WatchServiceFileSystemWatcher.FileChangeEvent.Type.ADDED) {
         try (FileInputStream in = new FileInputStream(change.getFile())) {
           byte[] bytes = FileReader.readFileBytes(in);
           ClassFile file = new ClassFile(new DataInputStream(new ByteArrayInputStream(bytes)));
           addedClasses.add(new AddedClass(file.getName(), bytes, classLoader));
         }
       } else if (change.getType()
           == WatchServiceFileSystemWatcher.FileChangeEvent.Type.MODIFIED) {
         String hash = hashes.get(change.getFile().getCanonicalPath());
         if (hash == null) {
           // class is not loaded yet
           continue;
         }
         try (FileInputStream in = new FileInputStream(change.getFile())) {
           byte[] bytes = FileReader.readFileBytes(in);
           if (!hash.equals(MD5.md5(bytes))) {
             ClassFile file =
                 new ClassFile(new DataInputStream(new ByteArrayInputStream(bytes)));
             changedClasses.add(
                 new ClassDefinition(classLoader.loadClass(file.getName()), bytes));
           }
         }
       }
     }
     Agent.redefine(
         changedClasses.toArray(new ClassDefinition[changedClasses.size()]),
         addedClasses.toArray(new AddedClass[addedClasses.size()]));
   } catch (Exception e) {
     e.printStackTrace();
   }
 }
 public static byte[] getIntegrationClass(ClassLoader c, String name) {
   if (!integrationClassloader.containsKey(c)) {
     return null;
   }
   URL resource =
       ClassLoader.getSystemClassLoader().getResource(name.replace('.', '/') + ".class");
   InputStream in = null;
   try {
     in = resource.openStream();
     return org.fakereplace.util.FileReader.readFileBytes(resource.openStream());
   } catch (Exception e) {
     throw new RuntimeException(e);
   } finally {
     try {
       if (in != null) {
         in.close();
       }
     } catch (IOException e) {
     }
   }
 }
  public synchronized void addClassFile(String className, ClassLoader classLoader) {
    if (classLoader == null) {
      return;
    }
    URL resource = classLoader.getResource(className.replace(".", "/") + ".class");
    if (resource == null) {
      return;
    }
    File file = new File(resource.getFile());
    if (!file.exists()) {
      return;
    }

    int parentCount = 1;
    for (int i = 0; i < className.length(); ++i) {
      if (className.charAt(i) == '.' || className.charAt(i) == '/') {
        parentCount++;
      }
    }
    try (InputStream in = resource.openStream()) {
      hashes.put(file.getCanonicalPath(), MD5.md5(FileReader.readFileBytes(in)));
    } catch (IOException e) {
      e.printStackTrace();
      return;
    }
    for (int i = 0; i < parentCount; ++i) {
      file = file.getParentFile();
    }
    if (registered.contains(file)) {
      return;
    }
    registered.add(file);

    Callback callback = callbacks.get(classLoader);
    if (callback == null) {
      callbacks.put(classLoader, callback = new Callback(classLoader));
    }
    watcher.watchPath(file, callback);
  }
    @Override
    public Iterable<JavaFileObject> list(
        Location location, String packageName, Set<Kind> kinds, boolean recurse)
        throws IOException {
      if (location == StandardLocation.PLATFORM_CLASS_PATH) {
        return standardJavaFileManager.list(location, packageName, kinds, recurse);
      }
      List<JavaFileObject> ret = new ArrayList<>();
      if (kinds.contains(Kind.CLASS) && location.equals(StandardLocation.CLASS_PATH)) {
        if (classLoader instanceof ModuleClassLoader) {
          ModuleClassLoader mcl = (ModuleClassLoader) classLoader;
          final String packageWithSlashes = packageName.replace(".", "/");
          try {
            Iterator<Resource> resources =
                mcl.getModule()
                    .iterateResources(
                        new PathFilter() {
                          @Override
                          public boolean accept(String path) {
                            if (recurse) {
                              return path.startsWith(packageWithSlashes);
                            } else {
                              return path.equals(packageWithSlashes);
                            }
                          }
                        });
            while (resources.hasNext()) {
              Resource res = resources.next();
              if (!res.getName().endsWith(".class")) {
                continue;
              }
              String binaryName =
                  res.getName().replace("/", ".").substring(0, res.getName().length() - 6);
              try {
                ret.add(
                    new ZipJavaFileObject(
                        org.fakereplace.util.FileReader.readFileBytes(res.openStream()),
                        binaryName,
                        res.getURL().toURI()));
              } catch (URISyntaxException e) {
                e.printStackTrace();
              }
            }
          } catch (ModuleLoadException e) {
            e.printStackTrace();
          }
        } else {
          URL res = classLoader.getResource(packageName.replace(".", "/"));
          if (res != null) {
            if (res.getProtocol().equals("file")) {
              Path dirPath = Paths.get(res.getFile());
              listDir(packageName, dirPath, recurse, ret);
            } else if (res.getProtocol().equals("jar")) {
              JarURLConnection connection = (JarURLConnection) res.openConnection();
              Enumeration<JarEntry> entryEnum = connection.getJarFile().entries();
              while (entryEnum.hasMoreElements()) {
                JarEntry entry = entryEnum.nextElement();
                String name = entry.getName();
                if (name.endsWith(".class") && !entry.isDirectory()) {
                  if (name.startsWith(packageName.replace(".", "/"))) {
                    String rem = name.substring(packageName.length());
                    if (rem.startsWith("/")) {
                      rem = rem.substring(1);
                    }
                    if (!recurse) {
                      if (rem.contains("/")) {
                        continue;
                      }
                    }
                    String binaryName =
                        entry
                            .getName()
                            .replace("/", ".")
                            .substring(0, entry.getName().length() - 6);
                    try {
                      URI uri = new URI(res.toExternalForm() + "/" + rem);
                      ret.add(
                          new ZipJavaFileObject(
                              org.fakereplace.util.FileReader.readFileBytes(
                                  uri.toURL().openStream()),
                              binaryName,
                              uri));
                    } catch (Exception e) {
                      e.printStackTrace();
                    }
                  }
                }
              }
            } else {
              System.err.println(
                  "Could not find package "
                      + packageName
                      + " in "
                      + classLoader
                      + " unknown protocol "
                      + res.getProtocol());
            }
          } else {
            return standardJavaFileManager.list(location, packageName, kinds, recurse);
          }
        }
      } else {
        return standardJavaFileManager.list(location, packageName, kinds, recurse);
      }

      return ret;
    }