diff --git a/src/main/java/net/flintmc/launcher/classloading/RootClassLoader.java b/src/main/java/net/flintmc/launcher/classloading/RootClassLoader.java index bf378e1..77c4c78 100644 --- a/src/main/java/net/flintmc/launcher/classloading/RootClassLoader.java +++ b/src/main/java/net/flintmc/launcher/classloading/RootClassLoader.java @@ -43,6 +43,7 @@ import net.flintmc.launcher.classloading.common.CommonClassLoader; import net.flintmc.launcher.classloading.common.CommonClassLoaderHelper; import net.flintmc.launcher.service.LauncherPlugin; +import net.flintmc.launcher.util.ClassPathEnumeration; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -296,16 +297,7 @@ public Class findClass(String name, ChildClassLoader preferredLoader) */ @Override public URL findResource(String name) { - return super.findResource(name); - // TODO: 5/18/2021 - // The findResource(String, boolean) method slows down the startup - // by 10-20 seconds on the Windows operating system. - // - // I think we still need a better solution for this, - // but since there are currently no launcher plugins that could redirect the resource, - // the implementation is fine. - // - // return findResource(name, true); + return this.findResource(name, true); } /** @@ -379,7 +371,7 @@ public URL findResource(String name, boolean allowRedirect) { */ @Override public Enumeration findResources(String name) throws IOException { - return findResources(name, true); + return this.findResources(name, true); } /** @@ -387,39 +379,14 @@ public Enumeration findResources(String name) throws IOException { * plugins. * * @param name The name of the resource to found - * @param allowRedirect Wether child plugins should be allowed to redirect the URL to a new one + * @param allowRedirect Whether child plugins should be allowed to redirect the URL to a new one * @return An enumeration of URL's pointing to resources matching the given name * @throws IOException If an I/O error occurs finding the resources * @see ClassLoader#findResources(String) */ public Enumeration findResources(String name, boolean allowRedirect) throws IOException { - // First search our own classpath - List resources = Collections.list(super.findResources(name)); - for (ChildClassLoader childClassLoader : children) { - // For every child as it for matching resources too - resources.addAll(Collections.list(childClassLoader.commonFindResources(name))); - } - - if (allowRedirect) { - // Redirection has been enabled, process every URL - List adjustedResources = new ArrayList<>(); - for (URL suggested : resources) { - for (LauncherPlugin plugin : plugins) { - URL newSuggestion = plugin.adjustResourceURL(name, suggested); - if (newSuggestion != null) { - // The plugin has applied a redirect, copy the new URL to the suggested one - suggested = newSuggestion; - } - } - - // Write down the final URL - adjustedResources.add(suggested); - } - - return Collections.enumeration(adjustedResources); - } else { - return Collections.enumeration(resources); - } + return new ClassPathEnumeration(name, super.findResources(name), this.children, + allowRedirect ? this.plugins : null); } /** diff --git a/src/main/java/net/flintmc/launcher/util/ClassPathEnumeration.java b/src/main/java/net/flintmc/launcher/util/ClassPathEnumeration.java new file mode 100644 index 0000000..307ac15 --- /dev/null +++ b/src/main/java/net/flintmc/launcher/util/ClassPathEnumeration.java @@ -0,0 +1,122 @@ +/* + * FlintMC + * Copyright (C) 2020-2021 LabyMedia GmbH and contributors + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package net.flintmc.launcher.util; + +import net.flintmc.launcher.classloading.ChildClassLoader; +import net.flintmc.launcher.service.LauncherPlugin; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.net.URL; +import java.util.*; + +/** + * Helper enumeration implementation for composing multiple class paths of different class loaders + * into a single resource search enumeration. + */ +public class ClassPathEnumeration implements Enumeration { + + private final String resourceName; + + private Enumeration currentEnumeration; + private final Iterator classLoaders; + private final Set resourcePathManipulators; + + private URL currentURL; + + /** + * Constructs and initializes a new class path search. + * + * @param resourceName The name of the resource to search + * @param startEnumeration The initial enumeration to search + * @param classLoaders Additional class loaders to search + * @param resourcePathManipulators The launcher plugins to call for manipulating the resource + * path, or {@code null}, if no resource path manipulations should + * be performed + */ + public ClassPathEnumeration( + String resourceName, + Enumeration startEnumeration, + List classLoaders, + Set resourcePathManipulators) { + this.resourceName = resourceName; + this.currentEnumeration = startEnumeration; + this.classLoaders = classLoaders.iterator(); + this.resourcePathManipulators = resourcePathManipulators; + } + + private boolean next() throws IOException { + if (this.currentURL != null) { + // We already have a custom URL, no need to go to the next one + return true; + } + + while (!this.currentEnumeration.hasMoreElements()) { + if (!this.classLoaders.hasNext()) { + // The current enumeration has no more elements, and there are no class loaders left + return false; + } + + // The current enumeration has no matching elements, query the next class loader + this.currentEnumeration = this.classLoaders.next().commonFindResources(resourceName); + } + + // The current enumeration has matching element(s), retrieve the next one + this.currentURL = this.currentEnumeration.nextElement(); + + if (this.resourcePathManipulators != null) { + // Let each launcher plugin manipulate the resource paths + for (LauncherPlugin manipulator : this.resourcePathManipulators) { + URL newURL = manipulator.adjustResourceURL(resourceName, this.currentURL); + + if (newURL != null) { + // Path has been overwritten + this.currentURL = newURL; + } + } + } + + return true; + } + + @Override + public boolean hasMoreElements() { + try { + return this.next(); + } catch (IOException exception) { + throw new UncheckedIOException(exception); + } + } + + @Override + public URL nextElement() { + try { + if (!this.next()) { + throw new NoSuchElementException(); + } + } catch (IOException exception) { + throw new UncheckedIOException(exception); + } + + // Take the current URL and clear it internally, so that the next() function will search again + URL tmp = this.currentURL; + this.currentURL = null; + return tmp; + } +}