Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,7 @@
* @since 0.0.1
*/
@API(since = "0.0.1", status = Status.INTERNAL)
public abstract class AbstractPackageContainerGroup
implements PackageContainerGroup {
public abstract class AbstractPackageContainerGroup implements PackageContainerGroup {

// https://docs.oracle.com/cd/E19830-01/819-4712/ablgz/index.html
private static final Set<String> ARCHIVE_EXTENSIONS = Set.of(
Expand Down Expand Up @@ -268,8 +267,10 @@ public void listFileObjects(
}
}

protected ContainerGroupClassLoaderImpl createClassLoader() {
return new ContainerGroupClassLoaderImpl(getLocation(), getPackages());
}

/**
* Create a classloader and return it.
*
* @return the classloader.
*/
protected abstract ClassLoader createClassLoader();
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,18 @@
*
* @author Ashley Scopes
* @since 0.0.1
* @deprecated use {@link ContainerGroupUrlClassLoader} instead. This class is kept for profiling
* later on to compare the performance of the newer URL class loader versus this one.
*/
@API(since = "0.0.1", status = Status.EXPERIMENTAL)
public class ContainerGroupClassLoaderImpl extends ClassLoader {
@API(since = "0.0.1", status = Status.DEPRECATED)
@Deprecated(forRemoval = true)
public class ContainerGroupPathClassLoader extends ClassLoader {

static {
ClassLoader.registerAsParallelCapable();
}

private static final Logger LOGGER = LoggerFactory.getLogger(ContainerGroupClassLoaderImpl.class);
private static final Logger LOGGER = LoggerFactory.getLogger(ContainerGroupPathClassLoader.class);
private static final List<Container> NO_PACKAGES = List.of();
private static final Map<String, ? extends List<Container>> NO_MODULES = Map.of();

Expand All @@ -62,7 +65,7 @@ public class ContainerGroupClassLoaderImpl extends ClassLoader {
* @param location the location that the containers are for.
* @param packageContainers the package containers.
*/
public ContainerGroupClassLoaderImpl(Location location,
public ContainerGroupPathClassLoader(Location location,
List<? extends Container> packageContainers) {
this(location, packageContainers, NO_MODULES);
}
Expand All @@ -73,7 +76,7 @@ public ContainerGroupClassLoaderImpl(Location location,
* @param location the location that the containers are for.
* @param moduleContainers the module containers.
*/
public ContainerGroupClassLoaderImpl(
public ContainerGroupPathClassLoader(
Location location,
Map<String, ? extends List<Container>> moduleContainers
) {
Expand All @@ -87,7 +90,7 @@ public ContainerGroupClassLoaderImpl(
* @param packageContainers the package containers.
* @param moduleContainers the module containers.
*/
public ContainerGroupClassLoaderImpl(
public ContainerGroupPathClassLoader(
Location location,
List<? extends Container> packageContainers,
Map<String, ? extends List<Container>> moduleContainers
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/*
* Copyright (C) 2022 - 2022 Ashley Scopes
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.github.ascopes.jct.containers.impl;

import io.github.ascopes.jct.containers.Container;
import io.github.ascopes.jct.containers.ModuleContainerGroup;
import io.github.ascopes.jct.containers.OutputContainerGroup;
import io.github.ascopes.jct.containers.PackageContainerGroup;
import io.github.ascopes.jct.pathwrappers.PathWrapper;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Collection;
import java.util.stream.Stream;
import org.apiguardian.api.API;
import org.apiguardian.api.API.Status;

/**
* An extension of the Java {@link URLClassLoader} that wraps around container groups.
*
* @author Ashley Scopes
* @since 0.0.1
*/
@API(since = "0.0.1", status = Status.INTERNAL)
public final class ContainerGroupUrlClassLoader extends URLClassLoader {

private ContainerGroupUrlClassLoader(String name, URL[] urls) {
super(name, urls, ClassLoader.getSystemClassLoader());
}

/**
* Create a class loader for a given package container group.
*
* @param group the package container group to wrap.
* @return the class loader.
*/
public static ContainerGroupUrlClassLoader createClassLoaderFor(PackageContainerGroup group) {
return new ContainerGroupUrlClassLoader(
"Packages within package group " + group.getLocation().getName(),
urlsForPackageGroup(group)
.distinct()
.toArray(URL[]::new)
);
}

/**
* Create a class loader for a given module container group.
*
* @param group the module container group to wrap.
* @return the class loader.
*/
public static ContainerGroupUrlClassLoader createClassLoaderFor(ModuleContainerGroup group) {
return new ContainerGroupUrlClassLoader(
"Packages within module group " + group.getLocation().getName(),
urlsForModuleGroup(group)
.distinct()
.toArray(URL[]::new)
);
}

/**
* Create a class loader for a given output container group.
*
* @param group the output container group to wrap.
* @return the class loader.
*/
public static ContainerGroupUrlClassLoader createClassLoaderFor(OutputContainerGroup group) {
return new ContainerGroupUrlClassLoader(
"Packages within output group " + group.getLocation().getName(),
Stream.concat(urlsForPackageGroup(group), urlsForModuleGroup(group))
.distinct()
.toArray(URL[]::new)
);
}

private static Stream<URL> urlsForPackageGroup(PackageContainerGroup group) {
return group
.getPackages()
.stream()
.map(Container::getPathWrapper)
.map(PathWrapper::getUrl);
}

private static Stream<URL> urlsForModuleGroup(ModuleContainerGroup group) {
return group
.getModules()
.values()
.stream()
.map(PackageContainerGroup::getPackages)
.flatMap(Collection::stream)
.map(Container::getPathWrapper)
.map(PathWrapper::getUrl);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@
import java.util.Map;
import java.util.ServiceLoader;
import java.util.Set;
import java.util.stream.Collectors;
import javax.tools.JavaFileManager.Location;
import org.apiguardian.api.API;
import org.apiguardian.api.API.Status;
Expand All @@ -54,7 +53,7 @@ public final class ModuleContainerGroupImpl implements ModuleContainerGroup {
private final Location location;
private final Map<ModuleLocation, @WillCloseWhenClosed ModulePackageContainerGroupImpl> modules;
private final String release;
private final Lazy<ContainerGroupClassLoaderImpl> classLoaderLazy;
private final Lazy<ClassLoader> classLoaderLazy;

/**
* Initialize this container group.
Expand Down Expand Up @@ -207,16 +206,8 @@ public String toString() {
.toString();
}

private ContainerGroupClassLoaderImpl createClassLoader() {
var moduleMapping = modules
.entrySet()
.stream()
.collect(Collectors.toUnmodifiableMap(
entry -> entry.getKey().getModuleName(),
entry -> entry.getValue().getPackages()
));

return new ContainerGroupClassLoaderImpl(location, moduleMapping);
private ClassLoader createClassLoader() {
return ContainerGroupUrlClassLoader.createClassLoaderFor(this);
}

private ModulePackageContainerGroupImpl newPackageGroup(ModuleLocation location) {
Expand All @@ -233,5 +224,10 @@ private static final class ModulePackageContainerGroupImpl
private ModulePackageContainerGroupImpl(ModuleLocation location, String release) {
super(location, release);
}

@Override
protected ClassLoader createClassLoader() {
return ContainerGroupUrlClassLoader.createClassLoaderFor(this);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import javax.tools.JavaFileManager.Location;
import javax.tools.JavaFileObject.Kind;
import org.apiguardian.api.API;
Expand Down Expand Up @@ -213,7 +212,7 @@ public List<Set<Location>> getLocationsForModules() {

@Override
public Map<ModuleLocation, PackageContainerGroup> getModules() {
return null;
return Map.copyOf(modules);
}

@Override
Expand All @@ -227,16 +226,8 @@ public boolean hasLocation(ModuleLocation location) {
}

@Override
protected ContainerGroupClassLoaderImpl createClassLoader() {
var moduleMapping = modules
.entrySet()
.stream()
.collect(Collectors.toUnmodifiableMap(
entry -> entry.getKey().getModuleName(),
entry -> entry.getValue().getPackages()
));

return new ContainerGroupClassLoaderImpl(location, getPackages(), moduleMapping);
protected ClassLoader createClassLoader() {
return ContainerGroupUrlClassLoader.createClassLoaderFor(this);
}

@SuppressWarnings("resource")
Expand Down Expand Up @@ -267,5 +258,10 @@ private static final class OutputPackageContainerGroupImpl
private OutputPackageContainerGroupImpl(Location location, String release) {
super(location, release);
}

@Override
protected ClassLoader createClassLoader() {
return ContainerGroupUrlClassLoader.createClassLoaderFor(this);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,9 @@ public PackageContainerGroupImpl(Location location, String release) {
);
}
}

@Override
protected ClassLoader createClassLoader() {
return ContainerGroupUrlClassLoader.createClassLoaderFor(this);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import io.github.ascopes.jct.annotations.CheckReturnValue;
import io.github.ascopes.jct.annotations.Nullable;
import io.github.ascopes.jct.annotations.WillClose;
import io.github.ascopes.jct.pathwrappers.impl.PathWrapperUtils;
import io.github.ascopes.jct.utils.GarbageDisposalUtils;
import io.github.ascopes.jct.utils.ToStringBuilder;
import java.io.BufferedInputStream;
Expand Down Expand Up @@ -72,6 +73,7 @@ public abstract class AbstractTestDirectory<I extends AbstractTestDirectory<I>>
private final Path rootDirectory;
private final String separator;
private final URI uri;
private final URL url;
private final Closeable closeHook;

@SuppressWarnings("ThisEscapedInObjectConstruction")
Expand All @@ -82,16 +84,19 @@ protected AbstractTestDirectory(
boolean closeOnGc,
Closeable closeHook
) {

// Register immediately so we still clean up if an exception is thrown from this constructor.
if (closeOnGc) {
LOGGER.trace("Registering {} to be destroyed on garbage collection", rootDirectory.toUri());
GarbageDisposalUtils.onPhantom(this, name, closeHook);
}

this.name = requireNonNull(name, "name");
this.closeHook = requireNonNull(closeHook, "closeHook");
this.rootDirectory = requireNonNull(rootDirectory, "rootDirectory");
this.separator = requireNonNull(separator, "separator");
uri = this.rootDirectory.toUri();

if (closeOnGc) {
LOGGER.trace("Registering {} to be destroyed on garbage collection", uri);
GarbageDisposalUtils.onPhantom(this, name, closeHook);
}
url = PathWrapperUtils.retrieveRequiredUrl(this.rootDirectory);
}


Expand Down Expand Up @@ -119,6 +124,12 @@ public URI getUri() {
return uri;
}

@CheckReturnValue
@Override
public URL getUrl() {
return url;
}

/**
* Get the identifying name of the temporary file system.
*
Expand Down Expand Up @@ -297,31 +308,6 @@ private I thisTestFileSystem() {
return (I) this;
}

/**
* Assert that the given name is a valid name for a directory, and that it does not contain
* potentially dangerous characters such as double-dots or slashes that could be used to escape
* the directory we are running from.
*
* @param name the directory name to check.
* @throws IllegalArgumentException if the name is invalid.
* @throws NullPointerException if the name is {@code null}.
*/
protected static void assertValidRootName(@Nullable String name) {
Objects.requireNonNull(name, "name");

if (name.isBlank()) {
throw new IllegalArgumentException("Directory name cannot be blank");
}

if (!name.equals(name.trim())) {
throw new IllegalArgumentException("Directory name cannot begin or end in spaces");
}

if (name.contains("/") || name.contains("\\") || name.contains("..")) {
throw new IllegalArgumentException("Invalid file name provided");
}
}

/**
* Chainable builder for creating individual files.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import io.github.ascopes.jct.annotations.Nullable;
import java.net.URI;
import java.net.URL;
import java.nio.file.FileSystem;
import java.nio.file.Path;
import org.apiguardian.api.API;
Expand Down Expand Up @@ -60,6 +61,13 @@ public interface PathWrapper {
*/
URI getUri();

/**
* Get a URL representation of this path-like object.
*
* @return the URL.
*/
URL getUrl();

/**
* Get the parent path wrapper, if there is one.
*
Expand Down
Loading