Skip to content

Commit

Permalink
Merge pull request #2 from FlintMC/fix-resource-enumeration
Browse files Browse the repository at this point in the history
Implement findResources using a custom enumeration
  • Loading branch information
R0bbyYT authored May 20, 2021
2 parents 414101b + b463c7b commit 96d3d7a
Show file tree
Hide file tree
Showing 2 changed files with 128 additions and 39 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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);
}

/**
Expand Down Expand Up @@ -379,47 +371,22 @@ public URL findResource(String name, boolean allowRedirect) {
*/
@Override
public Enumeration<URL> findResources(String name) throws IOException {
return findResources(name, true);
return this.findResources(name, true);
}

/**
* Extension of {@link ClassLoader#findResources(String)} allowing to disable redirects by launch
* 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<URL> findResources(String name, boolean allowRedirect) throws IOException {
// First search our own classpath
List<URL> 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<URL> 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);
}

/**
Expand Down
122 changes: 122 additions & 0 deletions src/main/java/net/flintmc/launcher/util/ClassPathEnumeration.java
Original file line number Diff line number Diff line change
@@ -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<URL> {

private final String resourceName;

private Enumeration<URL> currentEnumeration;
private final Iterator<ChildClassLoader> classLoaders;
private final Set<LauncherPlugin> 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<URL> startEnumeration,
List<ChildClassLoader> classLoaders,
Set<LauncherPlugin> 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;
}
}

0 comments on commit 96d3d7a

Please sign in to comment.