package dev.plex.module.loader; import com.google.common.collect.Lists; import dev.plex.Plex; import dev.plex.module.PlexModule; import dev.plex.module.PlexModuleFile; import org.apache.maven.repository.internal.MavenRepositorySystemUtils; import org.eclipse.aether.DefaultRepositorySystemSession; import org.eclipse.aether.RepositorySystem; import org.eclipse.aether.artifact.Artifact; import org.eclipse.aether.artifact.DefaultArtifact; import org.eclipse.aether.collection.CollectRequest; import org.eclipse.aether.connector.basic.BasicRepositoryConnectorFactory; import org.eclipse.aether.graph.Dependency; import org.eclipse.aether.impl.DefaultServiceLocator; import org.eclipse.aether.repository.LocalRepository; import org.eclipse.aether.repository.RemoteRepository; import org.eclipse.aether.repository.RepositoryPolicy; import org.eclipse.aether.resolution.ArtifactResult; import org.eclipse.aether.resolution.DependencyRequest; import org.eclipse.aether.resolution.DependencyResolutionException; import org.eclipse.aether.resolution.DependencyResult; import org.eclipse.aether.spi.connector.RepositoryConnectorFactory; import org.eclipse.aether.spi.connector.transport.TransporterFactory; import org.eclipse.aether.transfer.AbstractTransferListener; import org.eclipse.aether.transfer.TransferCancelledException; import org.eclipse.aether.transfer.TransferEvent; import org.eclipse.aether.transport.http.HttpTransporterFactory; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.File; import java.io.IOException; import java.net.JarURLConnection; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; import java.util.ArrayList; import java.util.Arrays; import java.util.Enumeration; import java.util.List; import java.util.jar.JarEntry; import java.util.jar.JarFile; import java.util.logging.Level; import java.util.logging.Logger; //TODO: doesn't work public class LibraryLoader { private final Logger logger; private final RepositorySystem repository; private final DefaultRepositorySystemSession session; private final List repositories; public LibraryLoader(@NotNull Logger logger) { this.logger = logger; DefaultServiceLocator locator = MavenRepositorySystemUtils.newServiceLocator(); locator.addService(RepositoryConnectorFactory.class, BasicRepositoryConnectorFactory.class); locator.addService(TransporterFactory.class, HttpTransporterFactory.class); this.repository = locator.getService(RepositorySystem.class); this.session = MavenRepositorySystemUtils.newSession(); session.setChecksumPolicy(RepositoryPolicy.CHECKSUM_POLICY_FAIL); session.setLocalRepositoryManager(repository.newLocalRepositoryManager(session, new LocalRepository("libraries"))); session.setTransferListener(new AbstractTransferListener() { @Override public void transferStarted(@NotNull TransferEvent event) throws TransferCancelledException { logger.log(Level.INFO, "Downloading {0}", event.getResource().getRepositoryUrl() + event.getResource().getResourceName()); } }); session.setReadOnly(); this.repositories = repository.newResolutionRepositories(session, Arrays.asList(new RemoteRepository.Builder("central", "default", "https://repo.maven.apache.org/maven2").build())); } @Nullable public ClassLoader createLoader(@NotNull PlexModule module, @NotNull PlexModuleFile moduleFile) { if (moduleFile.getLibraries().isEmpty()) { return null; } logger.log(Level.INFO, "Loading libraries for {0}", new Object[]{moduleFile.getName()}); logger.log(Level.INFO, "[{0}] Loading {1} libraries... please wait", new Object[] { moduleFile.getName(), moduleFile.getLibraries().size() }); List dependencies = new ArrayList<>(); List> classes = Lists.newArrayList(); List files = Lists.newArrayList(); for (String library : moduleFile.getLibraries()) { Artifact artifact = new DefaultArtifact(library); Dependency dependency = new Dependency(artifact, null); dependencies.add(dependency); } DependencyResult result; try { result = repository.resolveDependencies(session, new DependencyRequest(new CollectRequest((Dependency) null, dependencies, repositories), null)); } catch (DependencyResolutionException ex) { throw new RuntimeException("Error resolving libraries", ex); } List jarFiles = new ArrayList<>(); for (ArtifactResult artifact : result.getArtifactResults()) { File file = artifact.getArtifact().getFile(); files.add(file); URL url; try { url = file.toURI().toURL(); } catch (MalformedURLException ex) { throw new AssertionError(ex); } jarFiles.add(url); logger.log(Level.INFO, "[{0}] Loaded library {1}", new Object[] { moduleFile.getName(), file }); } /*List jarFiles = Lists.newArrayList(); List artifacts = Lists.newArrayList(); List> classes = new ArrayList<>(); for (String library : moduleFile.getLibraries()) { Artifact artifact = new DefaultArtifact(library); ArtifactRequest request = new ArtifactRequest(); request.setArtifact(artifact); request.addRepository(this.repositories.get(0)); try { ArtifactResult result = this.repository.resolveArtifact(this.session, request); artifact = result.getArtifact(); jarFiles.add(artifact.getFile().toURI().toURL()); logger.log(Level.INFO, "Loaded library {0} for {1}", new Object[]{ artifact.getFile().toURI().toURL().toString(), moduleFile.getName() }); artifacts.add(artifact); } catch (ArtifactResolutionException | MalformedURLException e) { e.printStackTrace(); } }*/ logger.log(Level.INFO, "Loaded {0} libraries for {1}", new Object[]{jarFiles.size(), moduleFile.getName()}); // jarFiles.forEach(jar -> new CustomClassLoader(jar, Plex.class.getClassLoader())); // jarFiles.forEach(jar -> new CustomClassLoader(jar, Plex.class.getClassLoader())); /*URLClassLoader loader = new URLClassLoader(jarFiles.toArray(URL[]::new), Plex.class.getClassLoader()); dependencies.forEach(artifact -> { ArrayList classNames; try { classNames = getClassNamesFromJar(new JarFile(artifact.getArtifact().getFile())); for (String className : classNames) { Class classToLoad = Class.forName(className, true, loader); classes.add(classToLoad); } } catch (Exception e) { e.printStackTrace(); } }); classes.forEach(clazz -> logger.log(Level.INFO, "Loading class {0}", new Object[]{clazz.getName()}));*/ jarFiles.forEach(url -> { JarURLConnection connection; try { URL url2 = new URL("jar:" + url.toString() + "!/"); /* connection = (JarURLConnection) url2.openConnection(); logger.log(Level.INFO, "Jar File: " + connection.getJarFileURL().toString());*/ } catch (IOException e) { e.printStackTrace(); } }); return new URLClassLoader(files.stream().map(File::toURI).map(uri -> { try { return uri.toURL(); } catch (MalformedURLException e) { e.printStackTrace(); return null; } }).toList().toArray(URL[]::new)/*jarFiles.stream().map(url -> { try { return new URL("jar:" + url.toString() + "!/"); } catch (MalformedURLException e) { e.printStackTrace(); return null; } }).toList().toArray(URL[]::new)*/, Plex.class.getClassLoader())/*new CustomClassLoader(jarFiles.toArray(URL[]::new), Plex.class.getClassLoader())*/; } /*public List> loadDependency(List paths) throws Exception { List> classes = new ArrayList<>(); for (Path path : paths) { URL url = path.toUri().toURL(); URLClassLoader child = new URLClassLoader(new URL[]{url}, this.getClass().getClassLoader()); ArrayList classNames = getClassNamesFromJar(path.toString()); for (String className : classNames) { Class classToLoad = Class.forName(className, true, child); classes.add(classToLoad); } } return classes; }*/ private ArrayList getClassNamesFromJar(JarFile file) throws Exception { ArrayList classNames = new ArrayList<>(); try { //Iterate through the contents of the jar file Enumeration entries = file.entries(); while (entries.hasMoreElements()) { JarEntry entry = entries.nextElement(); //Pick file that has the extension of .class if ((entry.getName().endsWith(".class"))) { String className = entry.getName().replaceAll("/", "\\."); String myClass = className.substring(0, className.lastIndexOf('.')); classNames.add(myClass); } } } catch (Exception e) { throw new Exception("Error while getting class names from jar", e); } return classNames; } }